#include <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <netinet/in.h>

#include "server_socket.c"

#define SERVERPORT 3333 
#define MAX_LINE 1024
#define FD_INIT_SIZE 128

// 用户态缓冲区，用于存储服务端需要发送的数据 
// 采用非阻塞写的话，数据一次性不一定发的完，
// 所以先存储在用户态缓冲区中
struct Buffer {
    int connect_fd;  // 客户端连接 socket 文件描述符
    char buffer[MAX_LINE];  //实际存储数据的缓冲区
	// buffer 中：
	// [0...readIndex) 区间数据已经写到客户端
	// [readIndex...writeIndex) 区间数据还未写到客户端
	// [writeIndex...MAX_LINE) 区间还未接收客户端的数据
    size_t writeIndex;    
    size_t readIndex;  
    int readable;           //是否可以读
};

// 初始化一个用户态缓冲区
struct Buffer *alloc_Buffer() {
    struct Buffer *buffer = malloc(sizeof(struct Buffer));
    if (!buffer)
        return NULL;
    buffer->connect_fd = 0;
    buffer->writeIndex = buffer->readIndex = buffer->readable = 0;
    return buffer;
}

// 释放指定缓冲区的内存
void free_Buffer(struct Buffer *buffer) {
    free(buffer);
}

// 字母大小写转换 
char process_char(char c) {
    if (c >= 'a' && c <= 'z') {
        return c - 32;
    } else if (c >= 'A' && c <= 'Z') {
        return c + 32;
    }
    return c;
}

int onSocketRead(int fd, struct Buffer *buffer) {
    char buf[1024];
    int i;
    ssize_t result;
    while (1) {
		// 非阻塞读取客户端数据到 buf 中
        result = read(fd, buf, sizeof(buf));
		// 没有读到数据，直接退出
        if (result <= 0)
            break;
		
		// 将读到的数据处理后，放到用户态缓冲区
        for (i = 0; i < result; ++i) {
            if (buffer->writeIndex < sizeof(buffer->buffer))
                buffer->buffer[buffer->writeIndex++] = process_char(buf[i]);
            if (buf[i] == '\n') {
                buffer->readable = 1;  //缓冲区可以读
            }
        }
    }
	
    if (result == 0) { // FIN 报文，调用方需要关闭当前连接 socket
        return 1;
    } else if (result < 0) {
        if (errno == EAGAIN) // 没有数据可读
            return 0;
		// 读的时候出错了
        return -1;
    }

    return 0;
}

int onSocketWrite(int fd, struct Buffer *buffer) {
    while (buffer->readIndex < buffer->writeIndex) {
        ssize_t result = write(fd, 
				buffer->buffer + buffer->readIndex, // 可写数据的起始位置
				buffer->writeIndex - buffer->readIndex); // 可写数据的长度
        
		if (result < 0) {
            if (errno == EAGAIN) // 发送内核态缓冲区没有空闲的地方
                return 0;
            return -1;
        }
		
		// 写完了 result 个字节
        buffer->readIndex += result;
    }
	
	// 没有可以写的数据了
    if (buffer->readIndex == buffer->writeIndex)
        buffer->readIndex = buffer->writeIndex = 0;

    buffer->readable = 0;

    return 0;
}

int main() {
    struct sockaddr_in cs;
    int a_size, i;
    int listen_socket_fd; 
	
	// 创建一个监听 socket
	listen_socket_fd = tcp_listen_socket(SERVERPORT);

	// 设置监听 socket 的 IO 为非阻塞 IO
	fcntl(listen_socket_fd, F_SETFL, O_NONBLOCK);
	
	// 初始化所有客户端连接 socket 缓存数据的缓冲区(用户态)
	struct Buffer *buffer[FD_INIT_SIZE];
    for (i = 0; i < FD_INIT_SIZE; ++i) {
        buffer[i] = alloc_Buffer();
    }	
	
	// 声明三个文件描述符集合
	fd_set readfds, writefds, exfds;
	int retval;

	// 记录当前最大的描述符
	int maxfd = listen_socket_fd;

	for (;;) {
		// 设置感兴趣的 fd 及其 IO 事件 
		// 每次都要设置，因为下面的 select 函数会修改这些描述符集合	
		FD_ZERO(&readfds); 
		FD_ZERO(&writefds); 
		FD_ZERO(&exfds);
		
		// 对监听 socket 的可读事件感兴趣
		FD_SET(listen_socket_fd, &readfds);
        
		for (i = 0; i < FD_INIT_SIZE; ++i) {
            if (buffer[i]->connect_fd > 0) {
				// 计算最大的 fd
                if (buffer[i]->connect_fd > maxfd)
                    maxfd = buffer[i]->connect_fd;
				// 对客户端连接 socket 的可读事件感兴趣
                FD_SET(buffer[i]->connect_fd, &readfds);
			
				// 如果服务端的用户态缓冲区数据可以读，
				// 那么将缓冲区数据写到客户端
                if (buffer[i]->readable) {
					// 对客户端连接 socket 的可写事件感兴趣
                    FD_SET(buffer[i]->connect_fd, &writefds);
                }
            }
        }
		
		// 通知内核，如果有我关心的 IO 事件发生的话，请通知我
		// 如果没有的话，我将一直阻塞下去，阻塞时放弃 CPU 执行权
		retval = select(maxfd + 1, &readfds, &writefds, &exfds, NULL);
		if (retval < 0) {
			perror("select error");
		}
		
		printf("wakeup from select \n");
				
		// 判断监听 socket 是否有连接进来（可读事件发生）
		if (FD_ISSET(listen_socket_fd, &readfds)) {
			int fd = accept(listen_socket_fd, (struct sockaddr*)&cs, &a_size);
    		if(fd == -1) {
        		perror("accept");
        		exit(1);
    		} else if (fd > FD_INIT_SIZE) { 
				// 大于服务器同时支持的最大的 socket 连接
				perror("too many connections");
				close(fd);
			} else {
    			printf("accept success! fd = %d\n", fd);
				// 将当前连接进来的 socket 设置为非阻塞
				fcntl(fd, F_SETFL, O_NONBLOCK);
				if (buffer[fd]->connect_fd == 0) {
                    buffer[fd]->connect_fd = fd;
                } else {
                    perror("connection already exists");
                }
			}
		}
		
		// 检测每个 fd，看看有没有感兴趣的 IO 事件发生
		for (i = 0; i < maxfd + 1; ++i) {
            int r = 0;
			// 监听 socket 关心的 IO 事件已经处理过了
            if (i == listen_socket_fd)
                continue;
			
			// 处理当前连接 socket 可读事件
            if (FD_ISSET(i, &readfds)) {
               // r = onSocketRead(i, buffer[i]);
               printf("get a readable event from fd = %d\n", i);
            }
			// 处理当前连接 socket 可写事件
            if (r == 0 && FD_ISSET(i, &writefds)) {
                r = onSocketWrite(i, buffer[i]);
            }
            if (r) { // 返回 1 的话，表示收到 FIN 报文，关闭连接
                buffer[i]->connect_fd = 0;
                close(i);
            }
        }
	}

	// 关闭服务端 server socket
    close(listen_socket_fd);
}
